﻿using AspectCore.DynamicProxy;
using Cyss.Core.Cache;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Cyss.Core.Attributes
{
    /// <summary>
    ///分布式 AOP 锁
    /// </summary>
    [AttributeUsage(AttributeTargets.Method)]
    public class LockAttribute : AbstractInterceptorAttribute
    {
        /// <summary>
        /// 锁key
        /// </summary>
        public string Key { set; get; }

        /// <summary>
        /// 是否等待
        /// </summary>
        public bool IsWait { set; get; }

        /// <summary>
        /// 到期时间秒
        /// </summary>
        public int Expiry { set; get; } = 120;

        /// <summary>
        /// 是否是参数级别锁
        /// </summary>
        public bool IsParameterLevel { set; get; }

        /// <summary>
        /// 提示
        /// </summary>
        public string Title { set; get; } = "操作有人正在执行请稍后在试";

        /// <summary>
        /// 
        /// </summary>
        private IStaticCacheManager _staticCacheManager { set; get; }


        public override async Task Invoke(AspectContext context, AspectDelegate next)
        {
            if (_staticCacheManager==null)
            {
                _staticCacheManager=IOCEngine.Resolve<IStaticCacheManager>();
            }
            if (string.IsNullOrWhiteSpace(this.Key))
            {
                this.Key =GenerateKey(context);
            }
            string token = Guid.NewGuid().ToString();
            string key = GetCacheKey(context.Parameters);
            var isLock = _staticCacheManager.LockTake(key, token, Expiry);
            if (isLock==false && IsWait)
            {
                isLock = _staticCacheManager.LockTake(key, token, Expiry);
                while (isLock==false)
                {
                    await Task.Delay(100);
                    isLock = _staticCacheManager.LockTake(key, token, Expiry);
                }
            }
            if (isLock)
            {
                await Next(context, next, key, token);
            }
            else
            {
                throw new Exception(this.Title);
            }
        }

        /// <summary>
        /// 生成Key
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private string GenerateKey(AspectContext context)
        {
            return context.ServiceMethod.DeclaringType.Name+"-"+context.ServiceMethod.Name;
        }

        /// <summary>
        /// 获取缓存key
        /// </summary>
        /// <param name="pars"></param>
        /// <returns></returns>
        private string GetCacheKey(object[] pars)
        {
            string key = "AopLock:"+this.Key;
            if (this.IsParameterLevel)
            {
                key=key+"-par:"+pars.FormatParameters();
            }
            return key;
        }

        private async Task Next(AspectContext context, AspectDelegate next, string key, string token)
        {
            try
            {
                await next(context);
            }
            finally
            {
                _staticCacheManager.LockRelease(key, token);
            }
        }
    }
}
